#include "Slave.h"
#include "Signals.h"

void Slave :: run() {
    listenForTerminationSignal();
    listenForBlockAssignment();
    while(shouldTerminate() == false) {
        resetWorkingBlockId();
        sendAssignmentRequest();
        storage -> nextIteration();
        performIteration();
    }
    storage -> stopServingBlocks();
}

void Slave :: performIteration() {
    listenForNextIterationSignal();
    while(shouldEndIteration() == false) {
        if(hasWorkAssigned()) {
            storage -> startGettingBlock(working_block_id);
            storage -> waitForBlocksAvailability();
            updateBlock();
            
            sendAssignmentRequest();
            listenForBlockAssignment();
        }
    }
}

void Slave :: resetWorkingBlockId() {
    working_block_id = -1;
}

void Slave :: serveBlocks() {
    storage -> startServingBlocks();
}

void Slave :: sendAssignmentRequest() {
    MPI_Send(&working_block_id, 1, MPI_INT, 0, Signals :: READY_TO_WORK, MPI_COMM_WORLD);
}

void Slave :: listenForNextIterationSignal() {
    MPI_Irecv(&nothing, 1, MPI_INT, 0, Signals :: NEXT_ITERATION, MPI_COMM_WORLD, &next_iteration_signal);
}

void Slave :: listenForTerminationSignal() {
    MPI_Irecv(&nothing, 1, MPI_INT, 0, Signals :: TERMINATE, MPI_COMM_WORLD, &termination_signal);
}

void Slave :: listenForBlockAssignment() {
    MPI_Irecv(&working_block_id, 1, MPI_INT, 0, Signals :: ASSIGNED_BLOCK, MPI_COMM_WORLD, &block_assignment);
}

bool Slave :: shouldEndIteration() {
    MPI_Test(&next_iteration_signal, &flag, &status);
    return flag;
}
bool Slave :: shouldTerminate() {
    MPI_Test(&termination_signal, &flag, &status);
    return flag;
}
bool Slave :: hasWorkAssigned() {
    MPI_Test(&block_assignment, &flag, &status);
    return flag;
}
